#coding=utf-8
'''
create on 2017-08-02

@author: sandy
'''
import random
import logging
class Frame(object):
    
    def __init__(self, buf):
        self.buf = buf
        self.msg = ""
        #掩码(客户端必须要有)
        self.mask = 0
        self.key = ""
        self.len = 0
        self.fin = 0
        self.payload = 0
        '''
        数据类型操作码4bit表示
        TEXT: 1, text类型的字符串 
        BINARY: 2,二进制数据，通常用来保存图片 
        CLOSE: 8,关闭连接的数据帧。 
        PING: 9, 心跳检测。ping 
        PONG: 10,心跳检测。pong
        '''
        self.opcode = 0 
        self.frame_length = 0
        self.isReady()

    def isReady(self):
        buf = self.buf
        if len(buf) < 2:
            logging.debug("Incomplet Frame: HEADER DATA")
        self.fin = ord(buf[0]) >> 7
        self.opcode = ord(buf[0]) & 0b1111
        self.payload = ord(buf[1]) & 0b1111111
        self.mask = ord(buf[1]) >> 7
        logging.debug("fin=%d opcode=%d payload=%d mask=%d",self.fin,self.opcode,self.payload,self.mask)
        buf = buf[2:]
        if self.payload < 126:
            self.len = self.payload
            if self.mask:
                self.frame_length = 6 + self.len
            else:
                self.frame_length = 2 + self.len
            if self.frame_length > len(self.buf):
                logging.debug("Incomplet Frame: FRAME DATA1")
            if len(buf) < 4 and self.mask:
                logging.debug("Incomplet Frame: KEY DATA1")
            if  self.mask:
                self.key = buf[:4]
                buf = buf[4:4+len(buf)+1]
            else:
                buf = buf[:self.len]

        elif self.payload == 126:
            if len(buf) < 6 and self.mask:
                logging.debug("Incomplet Frame: KEY DATA2")
            for k,i in [(0,1),(1,0)]:
                self.len += (ord(buf[k]) * 1 << (8*i))
            if self.mask:
                self.frame_length = 8 + self.len
            else:
                self.frame_length = 4 + self.len
            if self.frame_length > len(self.buf):
                logging.debug("Incomplet Frame: FRAME DATA2")
            buf = buf[2:]
            if self.mask:
                self.key = buf[:4]
                buf = buf[4:4+len(buf)+1]
            else:
                buf = buf[:self.len]
        else:
            if len(buf) < 10 and self.mask:
                logging.debug("Incomplet Frame: KEY DATA3")
            for k,i in [(0,7),(1,6),(2,5),(3,4),(4,3),(5,2),(6,1),(7,0)]:
                self.len += (ord(buf[k]) * 1 << (8*i))
            if self.mask:
                self.frame_length = 14 + self.len
            else:
                self.frame_length = 10 + self.len
            if self.frame_length > len(self.buf):
                logging.debug("Incomplet Frame: FRAME DATA3")
            buf = buf[8:]
            if self.mask:
                self.key = buf[:4]
                buf = buf[4:4+len(buf)+1]
            else:
                buf = buf[self.len]
        self.msg = buf

    def message(self):
        if not self.mask:
            return self.msg
        decoded_msg = ""
        for i in xrange(self.len):
            c = ord(self.msg[i]) ^ ord(self.key[i % 4])
            decoded_msg += str(chr(c))
        return decoded_msg

    def length(self):
        return self.frame_length

    @staticmethod
    def encodeMessage(buf, key):
        encoded_msg = ""
        buf_len = len(buf)
        for i in xrange(buf_len):
            c = ord(buf[i]) ^ ord(key[i % 4])
            encoded_msg += str(chr(c))
        return encoded_msg

    @staticmethod
    def buildMessage(buf,opcode = 1,mask=True):
        c_buf = buf
        msg = ""
        if mask:
            key = "".join([str(chr(random.randrange(1,255))) for i in xrange(4)])
        #first byte
        o = (1 << 7) + opcode
        msg += str(chr(o))
        #second byte
        buf_len = len(buf)
        if buf_len < 126:
            o = buf_len
            if mask:
                msg += str(chr(o + (1<<7)))
            else:
                msg += str(chr(o))
            if mask:
                msg += key
                msg += Frame.encodeMessage(buf,key)
            else:
                msg += buf
            return msg

        if buf_len <= ((1 << 16) - 1):
            if mask:
                msg += str(chr(126 + (1<<7)))
            else:
                msg += str(chr(126))
            for i in range(1,3):
                o = (buf_len >> (16 - (8*i))) & (2**8 - 1)
                msg += str(chr(o))
            if mask:
                msg += key
                msg += Frame.encodeMessage(buf,key)
            else:
                msg += buf
            return msg

        if buf_len <= ((1 << 64) - 1):
            if mask:
                msg += str(chr(127 + (1<<7)))
            else:
                msg += str(chr(127))
            for i in range(1,9):
                o = (buf_len >> (64 - (8*i))) & (2**8 - 1)
                msg += str(chr(o))
            if mask:
                msg += key
                msg += Frame.encodeMessage(buf,key)
            else:
                msg += buf
            return msg